/**
 * Kameo TypeScript SDK
 * 
 * This is a TypeScript wrapper around the Kameo WASM bindings that provides
 * a more ergonomic API for TypeScript/JavaScript applications.
 */

import __wbg_init, { KameoWasm as WasmKameo, JsActorRef } from './kameo-wasm';

// Export the KameoMessages namespace
export const KameoMessages = {
  /**
   * Example counter actor messages
   */
  Counter: {
    /**
     * Increment the counter
     */
    Inc: {} as { amount: number },

    /**
     * Get the current count
     */
    GetCount: {} as {}
  }
};

/**
 * ActorRef class for interacting with Kameo actors
 */
export class ActorRef<TMessage = any, TResponse = any> {
  private actorRef: JsActorRef;

  constructor(actorRef: JsActorRef) {
    this.actorRef = actorRef;
  }

  /**
   * Send a message to the actor and get a reply
   * @param message The message to send to the actor
   * @returns A promise that resolves to the actor's reply
   */
  async ask(message: TMessage): Promise<TResponse> {
    return await this.actorRef.ask(message);
  }

  /**
   * Send a message to the actor without expecting a reply
   * @param message The message to send to the actor
   * @returns A promise that resolves when the message is sent
   */
  async tell(message: TMessage): Promise<void> {
    return await this.actorRef.tell(message);
  }
}

// Initialize WASM module
let wasmInitialized = false;
let wasmInitPromise: Promise<void> | null = null;

async function initWasm() {
  if (wasmInitialized) return;
  
  if (!wasmInitPromise) {
    console.log("Initializing Kameo WASM bindings");
    wasmInitPromise = __wbg_init().then(() => {
      console.log("Kameo WASM bindings initialized successfully");
      wasmInitialized = true;
    }).catch(err => {
      console.error("Failed to initialize Kameo WASM bindings:", err);
      throw err;
    });
  }
  
  return wasmInitPromise;
}

/**
 * Main Kameo class for interacting with the actor system
 */
export class Kameo {
  private wasmKameo!: WasmKameo;
  
  /**
   * Initialize Kameo with WASM bindings
   */
  constructor() {
    // The constructor is now empty to avoid WASM initialization issues
  }

  /**
   * Initialize the Kameo instance
   * Must be called before using any other methods
   */
  async init(): Promise<void> {
    await initWasm();
    this.wasmKameo = new WasmKameo();
  }
  
  /**
   * Spawn a new actor
   * @param actorType The type of actor to spawn
   * @param initData Initial data to configure the actor
   * @returns A reference to the spawned actor
   */
  spawn<TMessage = any, TResponse = any>(actorType: string, initData: any): ActorRef<TMessage, TResponse> {
    if (!this.wasmKameo) {
      throw new Error('Kameo instance not initialized. Call init() first.');
    }
    const nativeActorRef = this.wasmKameo.spawn(actorType, initData);
    return new ActorRef<TMessage, TResponse>(nativeActorRef);
  }

  /**
   * Register an actor with a lookup name
   * @param actorRef The actor reference to register
   * @param name The name to register the actor under
   * @returns A promise that resolves when the actor is registered
   */
  async registerActor<TMessage = any, TResponse = any>(
    actorRef: ActorRef<TMessage, TResponse>,
    name: string
  ): Promise<void> {
    if (!this.wasmKameo) {
      throw new Error('Kameo instance not initialized. Call init() first.');
    }
    // Unwrap the ActorRef to get the native JsActorRef
    const nativeActorRef = (actorRef as any).actorRef;
    await this.wasmKameo.register_actor(nativeActorRef, name);
  }

  /**
   * Look up an actor by name
   * @param actorType The type of actor to look up
   * @param name The name of the actor to look up
   * @returns A promise that resolves to the actor reference, if found
   */
  async lookupActor<TMessage = any, TResponse = any>(
    actorType: string,
    name: string
  ): Promise<ActorRef<TMessage, TResponse>> {
    if (!this.wasmKameo) {
      throw new Error('Kameo instance not initialized. Call init() first.');
    }
    const nativeActorRef = await this.wasmKameo.lookup_actor(actorType, name);
    return new ActorRef<TMessage, TResponse>(nativeActorRef);
  }
}

// Export a default Kameo instance for convenience
export const kameo = new Kameo();
export default kameo;
